#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <string.h>
#pragma comment(lib, "ws2_32.lib")

//long类型的ip转string
void long_to_char_ip(unsigned long ip, char *str, int len)
{
	unsigned char *p = &ip;
	//将每个字节的内容取出来
	if (len < 16)
	{
		printf("long类型ip转string失败,str空间太小");
		return;
	}
	//printf("%u.%u.%u.%u\n", *p, *(p + 1), *(p + 2), *(p + 3));
	sprintf_s(str, len,"%u.%u.%u.%u", *p, *(p + 1), *(p + 2), *(p + 3));
}
//发送
int my_socket_send(char *ip)
{
	printf("目标ip:%s\n",ip);
	//初始化socket
	WORD ver;
	WSADATA wsa_data;
	ver = MAKEWORD(1, 1);//socket的版本 1.1
	WSAStartup(ver, &wsa_data);//windows要求,只要用socket，第一步必须调用这个函数


	/*
		创建一个sockte:
		int socket(int domain, int type, int protocol);
		domain指明所使用的协议族，通常为PF_INET/AF_INET，表示互联网协议族（TCP/IP协议族）；
		type参数指 定socket的类型：SOCK_STREAM （TCP）或SOCK_DGRAM（UDP），Socket接口还定义了原始Socket（SOCK_RAW），允许程序使用低层协议；
		protocol通常赋值"0"。
		Socket()调用返回一个整型socket描述符，你可以在后面 的调用使用它。
	*/
	SOCKET st = socket(AF_INET, SOCK_DGRAM, 0);

	//定义一个结构体(封装地址)
	struct sockaddr_in addr;
	//初始化结构体
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;//代表要使用TCP/IP的地址
	//设置目标端口 将整型变量从主机字节顺序转变成网络字节顺序
	addr.sin_port = htons(8080);//host to net short
	//设置目标IP地址 127.0.0.1代表本机
	inet_pton(AF_INET, ip, &addr.sin_addr);

	//发送消息
	char buf[1024] = {0};
	while (1)
	{
		memset(buf,0,1024);//清空
		//键盘获取
		gets(buf);
		if (buf[0] == '0')
		{
			break;
		}
		//发送,返回值表示发送了多少数据
		size_t rc = sendto(st, buf, strlen(buf), 0, (struct sockaddr *)&addr, sizeof(addr));
		printf("已发送%dB\n", rc);
	}

	//关闭socket
	closesocket(st);
	WSACleanup();//释放相关资源

	return 0;
}

//接收
int my_socket_receive()
{
	printf("等待接收数据...");
	//初始化socket
	WORD ver;
	WSADATA wsa_data;
	ver = MAKEWORD(1, 1);//socket的版本 1.1
	WSAStartup(ver, &wsa_data);//windows要求,只要用socket，第一步必须调用这个函数
	/*
	创建一个sockte:
	int socket(int domain, int type, int protocol);
	domain指明所使用的协议族，通常为PF_INET/AF_INET，表示互联网协议族（TCP/IP协议族）；
	type参数指 定socket的类型：SOCK_STREAM （TCP）或SOCK_DGRAM（UDP），Socket接口还定义了原始Socket（SOCK_RAW），允许程序使用低层协议；
	protocol通常赋值"0"。
	Socket()调用返回一个整型socket描述符，你可以在后面 的调用使用它。
	*/
	SOCKET st = socket(AF_INET, SOCK_DGRAM, 0);

	//定义一个结构体(封装地址)
	struct sockaddr_in addr;
	//初始化结构体
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;//代表要使用TCP/IP的地址
	//设置目标端口 将整型变量从主机字节顺序转变成网络字节顺序
	addr.sin_port = htons(8080);//host to net short
	//作为接收方,不需要指定Ip,接收方的主机是什么ip,就在什么ip上收数据
	addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//host to net long
	//将程序和端口号绑定(前提是该端口没有被其他程序使用)
	int result = bind(st, (struct sockaddr *)&addr, sizeof(addr));
	//绑定端口失败
	if (result == -1)
	{
		printf("绑定端口失败");
	}
		//绑定成功
	else
	{
		//接收数据
		char buf[1024] = {0};
		//发送方的iP
		struct sockaddr_in send_addr;
		memset(&send_addr, 0, sizeof(send_addr));
		int len = sizeof(send_addr);
		//接收
		while (1)
		{
			//清空缓冲区
			memset(buf,0,1024);
			size_t re = recvfrom(st, buf, sizeof(buf), 0, (struct sockaddr *)&send_addr, &len);
			//ip
			char char_ip[16] = { 0 };
			//将long类型的ip转为char[]
			long_to_char_ip(send_addr.sin_addr.S_un.S_addr, char_ip, 16);
			//printf("\n发送方ip:%u,%s\n", send_addr.sin_addr.S_un.S_addr, char_ip);
			//inet_ntoa(send_addr.sin_addr):获取字符串类型的ip(该函数多线程会有冲突)
			printf("\n发送方ip:%u,%s\n", send_addr.sin_addr.S_un.S_addr, inet_ntoa(send_addr.sin_addr));
			printf("发送方端口号:%d\n", send_addr.sin_port);
			printf("接收到%d B\n", re);
			printf("内容:%s\n", buf);
		}

	}

	//关闭socket
	closesocket(st);
	WSACleanup();//释放相关资源
	return 0;
}

//解析ip地址
void test_ip()
{
	unsigned long addr = inet_addr("192.168.2.102");
	printf("%u\n",addr);
	//addr long类型占4个字节
	unsigned char *p = &addr;
	//将每个字节的内容取出来
	printf("%u.%u.%u.%u\n",*p,*(p+1),*(p+2),*(p+3));//192.168.2.102
}

